#ifndef __M_QUEUE_H__
#define __M_QUEUE_H__

#include "../mqcommon/logger.hpp"
#include "../mqcommon/helpper.hpp"
#include "../mqcommon/msg.pb.h"

#include <iostream>
#include <unordered_map>
#include <mutex>
#include <memory>

namespace mmq
{

    struct MsgQueue
    {
    public:
        using ptr = std::shared_ptr<MsgQueue>;

    public:
        MsgQueue() {}
        MsgQueue(const std::string &name, bool durable, bool exclusive, bool auto_delete,
                const google::protobuf::Map<std::string, std::string> &args)
            : _name(name), _durable(durable), _exclusive(exclusive), _auto_delete(auto_delete), _args(args)
        {
        }

        void SetArgs(const std::string &str_args)
        {
            std::vector<std::string> result;
            StrHelper::Split(str_args, "&", result);
            for (auto &str : result)
            {
                size_t pos = str.find('=');
                std::string key = str.substr(0, pos);
                std::string val = str.substr(pos + 1);
                // _args.insert({key, val});
                _args[key] = val;
            }
        }

        std::string GetArgs()
        {
            std::string ret;
            for (auto it = _args.begin(); it != _args.end(); ++it)
            {
                ret += it->first + "=" + it->second + "&";
            }
            return ret;
        }

    public:
        std::string _name;
        bool _durable;   // 是否持久化
        bool _exclusive; // 是否独占
        bool _auto_delete;
        // std::unordered_map<std::string, std::string> _args; // 其它参数
        google::protobuf::Map<std::string, std::string> _args;
    };


    using QueueMap = std::unordered_map<std::string, MsgQueue::ptr>;
    class MsgQueueMapper
    {
    public:
        MsgQueueMapper(const std::string &dbfile) : _sql_helper(dbfile)
        {
            std::string pPath = FileHelper::ParentDirectory(dbfile);
            FileHelper::CreateDirectory(pPath);
            _sql_helper.Open();
            CreateTable();
        }

        void CreateTable()
        {
            std::stringstream ss;
            ss << "create table if not exists queue_table(";
            ss << "name varchar(32) primary key, ";
            ss << "durable int, ";
            ss << "exclusive int, ";
            ss << "auto_delete int, ";
            ss << "args varchar(128));";
            assert(_sql_helper.Exec(ss.str(), nullptr, nullptr));
        }

        void RemoveTable()
        {
            std::string sql = "drop table if exists queue_table;";
            bool ret = _sql_helper.Exec(sql, nullptr, nullptr);
            if (ret == false)
            {
                ELOG("删除队列表失败！");
                abort();
            }
        }

        bool Insert(MsgQueue::ptr &mqp)
        {
            std::stringstream ss;
            ss << "insert into queue_table values(";
            ss << "'" << mqp->_name << "', ";
            ss << "'" << mqp->_durable << "', ";
            ss << "'" << mqp->_exclusive << "', ";
            ss << "'" << mqp->_auto_delete << "', ";
            ss << "'" << mqp->GetArgs() << "');";
            return _sql_helper.Exec(ss.str(), nullptr, nullptr);
        }

        void Remove(const std::string &name)
        {
            std::stringstream ss;
            ss << "delete from queue_table where name=";
            ss << "'" << name << "';";
            _sql_helper.Exec(ss.str(), nullptr, nullptr);
        }

        QueueMap Recovery()
        {
            std::string sql = "select name, durable, exclusive, auto_delete, args from queue_table;";
            QueueMap result;
            _sql_helper.Exec(sql, SelectCallback, &result);
            return result;
        }

    private:
        static int SelectCallback(void *arg, int colnum, char **row, char **fields)
        {
            QueueMap *result = (QueueMap *)arg;
            MsgQueue::ptr mqp = std::make_shared<MsgQueue>();
            mqp->_name = row[0];
            mqp->_durable = (bool)std::stoi(row[1]);
            mqp->_exclusive = (bool)std::stoi(row[2]);
            mqp->_auto_delete = (bool)std::stoi(row[3]);
            if (row[4])
                mqp->SetArgs(row[4]);
            result->insert({mqp->_name, mqp});
            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };

    class MsgQueueManager
    {
    public:
        using ptr = std::shared_ptr<MsgQueueManager>;
    public:
        MsgQueueManager(const std::string &dbfile) : _mapper(dbfile)
        {
            _msg_queues = _mapper.Recovery();
        }

        bool DeclareQueue(const std::string &name, bool durable, bool exclusive, bool auto_delete, 
            const google::protobuf::Map<std::string, std::string> &args)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            if(it != _msg_queues.end())
            {
                return true;
            }
            MsgQueue::ptr mqp = std::make_shared<MsgQueue>(name, durable, exclusive, auto_delete, args);
            if(mqp->_durable == true)
            {
                bool ret =  _mapper.Insert(mqp);
                if(ret == false) return false;
            }
            _msg_queues.insert({mqp->_name, mqp});
            return true;
        }

        void DeleteQueue(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            if(it == _msg_queues.end())
            {
                return;
            }
            if(it->second->_durable == true)
            {
                _mapper.Remove(name);
            }
            _msg_queues.erase(name);
        }

        MsgQueue::ptr SelectQueue(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            if(it == _msg_queues.end())
            {
                return MsgQueue::ptr();
            }
            return it->second;
        }

        QueueMap AllQueue() // 线程安全问题，不能返回引用
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _msg_queues;
        }

        size_t Size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _msg_queues.size();
        }

        void Clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.RemoveTable();
            _msg_queues.clear();
        }

        bool Exists(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            if(it == _msg_queues.end()) return false;
            return true;
        }
    
    private:
        std::mutex _mutex;
        MsgQueueMapper _mapper;
        QueueMap _msg_queues;
    };

}
#endif